package ru.niir.goodfil.db;

import ru.niir.goodfil.db.handlers.Goodfil;
import ru.niir.goodfil.utils.SumFormat;

import javax.persistence.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import static ru.niir.goodfil.utils.Utils.s;

/**
 * Группа товаров
 * <p/>
 * TRIM_FIX
 * NULL_FIX
 */
@Entity
@Table(name = "GoodTypes")
public class GoodType implements Unique , Serializable {
    /**
     * Идентификатор
     */
    @Id
    @GeneratedValue
    @Column(name = "`Id`", nullable = false)
    private long id;

    /**
     * Идентификатор родительской группы
     */
    @Column(name = "`IdParent`", nullable = false)
    private Long parentId;

    /**
     * Наименование группы товаров
     */
    @Column(name = "`Name`", nullable = false, length = 500)
    private String name;

    /**
     * Флаг фиктивного удаления группы товаров
     */
    @Column(name = "`IsDeleted`", nullable = false)
    private boolean deleted;

    /**
     * Идентификатор брэнда
     */
    @Column(name = "`IdBrand`", nullable = false)
    private Long brandId;

    /**
     * Товары внутри данной группы товаров
     */
    @Transient
    List<Good> goods = new ArrayList<Good>();

    /**
     * Дочерний группы товаров
     */
    @Transient
    List<GoodType> children = new ArrayList<GoodType>();

    /**
     * Родительская группа товаров
     */
    @Transient
    private GoodType parent;

    /**
     * Уровень вложенности данной группы товаров
     */
    @Transient
    private int level;

    @Transient
    private long absence;

    @Transient
    private double absenceCost;

    public double getAbsenceCost() {
        return absenceCost;
    }

    public void setAbsenceCost(double absenceCost) {
        this.absenceCost = absenceCost;
    }

    public String getAbsenceCostAsString(){
        return SumFormat.formatSum("###,###,##0.00", absenceCost);
    }

    /**
     * Установить идентификатор группы товаров
     */
    public void setId(long id) {
        this.id = id;
    }

    /**
     * Получить идентификатор группы товаров
     */
    public long getId() {
        return id;
    }

    /**
     * Получить идентификатор родительсмкой группы
     */
    public Long getParentId() {
        return parentId;
    }

    /**
     * Установить идентификато родитиельской группы
     */
    public void setParentId(Long parentId) {
        this.parentId = parentId;
    }

    /**
     * Получить наименование группы
     */
    public String getName() {
        return name;
    }

    /**
     * Установить наименование группы
     */
    public void setName(String name) {
        this.name = s(name);
    }

    /**
     * Получить список товаров в данной группе
     */
    public List<Good> getGoods() {
        return goods;
    }

    /**
     * Установить список товаров для данной группы
     */
    public void setGoods(List<Good> goods) {
        assert goods != null;
        for (Good good : goods) assert good.getGoodTypeId() == id;

        this.goods = goods;
    }

    /**
     * Получить список дочерних групп товаров для данной
     */
    public List<GoodType> getChildren() {
        return children;
    }

    /**
     * Установить список дочерних групп товаров для данной
     */
    public void setChildren(List<GoodType> children) {
        assert children != null;
        for (GoodType goodType : children) assert goodType.getParentId().longValue() == id;

        this.children = children;
    }

    /**
     * Получить родительскую группу для данной
     */
    public GoodType getParent() {
        return parent;
    }

    /**
     * Установить родительскую группу для данной
     */
    public void setParent(GoodType parent) {
        assert parent != null;
        assert parent.getId() == parentId;

        this.parent = parent;
    }

    /**
     * Получить уровень вложенности данной группы относительно корневой группы
     */
    public int getLevel() {
        return level;
    }

    /**
     * Установить уровень вложенности данной группы относительно корневой
     */
    public void setLevel(int level) {
        this.level = level;
        for (GoodType goodType : children) goodType.setLevel(level + 1);
    }

    /**
     * Получить массив, содержащий данный узел (в позиции 0) и все его подузлы (рекурсивно)
     */
    public List<GoodType> collect() {
        List<GoodType> result = new ArrayList<GoodType>();
        result.add(this);
        Collections.sort(children, new Comparator<GoodType>() {
            @Override
            public int compare(GoodType gt1, GoodType gt2) {
                return gt1.getName().compareTo(gt2.getName());
            }
        });
        for (GoodType node : children) result.addAll(node.collect());
        return result;
    }

    /**
     * Получить массив, содержащий данный узел (в позиции 0) и все его подузлы (рекурсивно)
     * ИГНОРИРУЯ ПРИ ЭТОМ ФИКТИВНО УДАЛЕННЫЕ УЗЛЫ
     */
    public List<GoodType> collectNotDeleted() {
        List<GoodType> result = new ArrayList<GoodType>();
        if (!deleted) {
            result.add(this);
            Collections.sort(children, new Comparator<GoodType>() {
                @Override
                public int compare(GoodType gt1, GoodType gt2) {
                    return gt1.getName().compareTo(gt2.getName());
                }
            });
            for (GoodType node : children) result.addAll(node.collectNotDeleted());
        }
        return result;
    }

    /**
     * Получить количество дочерних узлов
     */
    public int getChildrenCount() {
        if (children == null) return 0;
        else return children.size();
    }

    /**
     * Возвращает значение флага фиктивного удаления группы
     *
     * @return значение флага фиктивного удаления группы
     */
    public boolean isDeleted() {
        return deleted;
    }

    public Long getBrandId() {
        return brandId;
    }

    public void setBrandId(Long brandId) {
        this.brandId = brandId;
    }

    /**
     * Установить значение флага фиктивного удаления группы
     *
     * @return значение флага фиктивного удаления группы
     */
    public void setDeleted(boolean deleted) {
        this.deleted = deleted;
    }

    public boolean isRoot() {
        return id == 1;
    }

    public long getAbsence() {
        return absence;
    }

    public void setAbsence(long absence) {
        this.absence = absence;
    }

    @Override
    public String toString() {
        return "GoodType{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}
